import SceneController, { DirectionCode, FingerStyle, HandAnimaName, KeyEventParameters, MoveInfo, SceneState, SceneUtils, TButton, TLayout } from "./SceneControl";
import Scene from "./Scene";


const { ccclass, property } = cc._decorator;

export enum MoveState {
    onlyUpDown,
    onlyLeftRight,
    all,
}

export default abstract class LevelScene extends Scene {
    btn: TButton;
    progress: number = 0;
    handNode: cc.Node;
    moveState: MoveState = MoveState.all;
    catchNode: cc.Node;
    tw: cc.Tween<{ moveSpeed: number; }>;
    moveSpeed: { moveSpeed: number } = { moveSpeed: 0 };
    currLevelName: string;
    moveRangeRect: cc.Rect;
    levelStop: boolean;

    onBack: (() => void) | cc.Node;

    public static curLevelScene: LevelScene = null;
    startLocation: cc.Vec3;
    /**
     * 抓住时的浮动action
     */
    btnOffsetAction: cc.Tween<any>;

    current_touch: cc.Touch;

    protected playHandCatchingAction() {
        if (!this.btnOffsetAction) {
            this.btnOffsetAction = cc.tween(this.btn.offset)
                .by(0.25, { y: -8 })
                .by(0.5, { y: 16 })
                .by(0.25, { y: -8 })
                .union()
                .repeatForever();
        }
        this.btnOffsetAction.start();
    }

    protected stopHandCatchingAction() {
        this.btnOffsetAction?.stop();
        this.btnOffsetAction = null;
    }

    protected onLoad(): void {
        this.currLevelName = this.node.name;
        LevelScene.curLevelScene = this;
    }

    protected onDisable(): void {
        LevelScene.curLevelScene = null;
        SceneController.instance.removeHandAnim();
        this.unscheduleAllCallbacks();
        this.btnOffsetAction?.stop(); 
        super.onDisable();
    }

    protected getToosNodes(): cc.Node[] {
        return null;
    }

    /**
     * 获取提示节点
     * 此方法可被子类重写以返回具体的提示节点
     * @returns 提示节点，如果没有则返回 null
     */
    protected getTipNode(): cc.Node {
        // 默认实现返回 null，子类可重写此方法
        return null;
    }

    protected initBtn() {
        this.btn = new TButton(this.node, [DirectionCode.BACK]);
        let lastPos: cc.Vec2 | null = null;

        // 提取方向键判断逻辑
        const isDirectionKey = (code: DirectionCode) => [
            DirectionCode.LEFT,
            DirectionCode.RIGHT,
            DirectionCode.UP,
            DirectionCode.DOWN
        ].includes(code);

        // 提取移动按钮位置的逻辑
        const moveButton = (direction: DirectionCode, step: number) => {
            switch (direction) {
                case DirectionCode.LEFT:
                    if (this.moveState === MoveState.all || this.moveState === MoveState.onlyLeftRight) {
                        this.btn.offset.x = Math.max(this.btn.offset.x - step, this.moveRangeRect.xMin);
                    }
                    break;
                case DirectionCode.RIGHT:
                    if (this.moveState === MoveState.all || this.moveState === MoveState.onlyLeftRight) {
                        this.btn.offset.x = Math.min(this.btn.offset.x + step, this.moveRangeRect.xMax);
                    }
                    break;
                case DirectionCode.UP:
                    if (this.moveState === MoveState.all || this.moveState === MoveState.onlyUpDown) {
                        this.btn.offset.y = Math.min(this.btn.offset.y + step, this.moveRangeRect.yMax);
                    }
                    break;
                case DirectionCode.DOWN:
                    if (this.moveState === MoveState.all || this.moveState === MoveState.onlyUpDown) {
                        this.btn.offset.y = Math.max(this.btn.offset.y - step, this.moveRangeRect.yMin);
                    }
                    break;
            }
        };

        // 提取触发触摸移动事件的逻辑
        const triggerTouchMoveEvent = () => {
            if (this.catchNode) {
                const handSite: cc.Vec2 = this.handNode.convertToWorldSpaceAR(cc.Vec2.ZERO);
                if (!lastPos) lastPos = cc.v2(handSite.x, handSite.y);
                const touchEvent = this.getTouchEvent(cc.Node.EventType.TOUCH_MOVE);
                this.catchNode.dispatchEvent(touchEvent);
            }
        };

        this.btn.addKeyDownEvent((para: KeyEventParameters) => {
            if (this.levelStop) return;
            if (isDirectionKey(para.DirectionCode)) {
                this.tw = cc.tween(this.moveSpeed).to(0.8, { moveSpeed: 1200 }).start();
                this.stopHandCatchingAction();
            } else if (para.DirectionCode === DirectionCode.ENTER) {
                if (this.catchNode) {
                    this.moveState = MoveState.all;
                    this.catchNode.dispatchEvent(this.getTouchEvent(cc.Node.EventType.TOUCH_END));
                    this.stopHandCatchingAction();
                    this.catchNode = null;
                    this.startLocation = null;
                    this.playHandLoosenAnim(() => {
                        this.playHandBreathingAnim();
                    });
                    return;
                } else {
                    this.levelStop = true;
                    this.playHandCatchAnim(() => {
                        this.levelStop = false;
                        if (this.catchNode) {
                            this.playHandCatchingAction();
                        } else {
                            this.playHandLoosenAnim(() => {
                                this.stopHandCatchingAction();
                                this.playHandBreathingAnim();
                            });
                        }
                    });
                    this.addBtnEventKeyDown(para);
                    if (this.catchNode) {
                        this.startLocation = this.handNode.position;
                        this.catchNode.dispatchEvent(this.getTouchEvent(cc.Node.EventType.TOUCH_START));
                    }
                }
            }
        });

        this.btn.addKeyLongPressEvent((para: KeyEventParameters, dt: number) => {
            if (this.levelStop) return;
            const step = dt * this.moveSpeed.moveSpeed;
            moveButton(para.DirectionCode, step);

            if (this.catchNode && isDirectionKey(para.DirectionCode)) {
                triggerTouchMoveEvent();
            }
            this.addBtnEventKeyLongPress(para, dt);
        });

        this.btn.addKeyUpEvent((para: KeyEventParameters) => {
            if (this.levelStop) return;
            if (isDirectionKey(para.DirectionCode)) {
                this.tw.stop();
                this.moveSpeed.moveSpeed = 0;
                if (this.catchNode) {
                    this.playHandCatchingAction();
                }
            }
            this.addBtnEventKeyUp(para);
        });

        this.btn.addFingerOutEvent((para: MoveInfo) => {
            this.onLoseFocus();
        });

        this.btn.addBeforeFingerInEvent((para: MoveInfo) => {
            this.onFocus();
        });

        this.handNode.on(cc.Node.EventType.POSITION_CHANGED, () => {
            triggerTouchMoveEvent();
        }, this);
    }

    protected abstract initActiveObject();
    protected abstract addBtnEventKeyDown(para: KeyEventParameters);
    protected abstract addBtnEventKeyLongPress(para: KeyEventParameters, dt: number);
    protected abstract addBtnEventKeyUp(para: KeyEventParameters);

    protected onFocus() {
        if (!this.catchNode)
            this.playHandBreathingAnim();
        this.levelStop = false;
    }

    protected onLoseFocus() {
        SceneController.instance.removeHandAnim();
        this.levelStop = false;
    }

    public nextStep() {
        this.progress++;
    }

    public initScene() {
        this.moveRangeRect = cc.rect(-620, -340, 1240, 700);
        this.handNode = SceneController.instance.handNode;
        this.levelStop = false;
        this.initActiveObject()
        this.initBtn();

        let layout = new TLayout();
        layout.buttonList = [
            [this.btn]
        ];
        this.tscene.addLayout(layout);
        this.tscene.sceneState = SceneState.normal;
        this.tscene.onBack = () => {
            if (this.onBack) {
                if (this.onBack instanceof cc.Node) {
                    SceneUtils.emitButton(this.onBack);
                } else {
                    this.onBack();
                }
            }
        }
        this.tscene.fingerStyle = FingerStyle.hand;
        this.tscene.joinController();
        this.playHandBreathingAnim();
    }

    show() {
        this.levelStop = false;
        this.tscene.sceneState = SceneState.normal;
        this.tscene.apply();
    }

    hide() {
        this.catchNode = null;
        this.levelStop = true;
        this.tscene.sceneState = SceneState.stop;
        this.tscene.apply();
    }

    playHandCatchAnim(callBack?: () => void) {
        SceneController.instance.playHandAnim(HandAnimaName.handCatch, callBack);
    }

    playHandBreathingAnim() {
        SceneController.instance.playHandAnim(HandAnimaName.handBreathing);
    }

    playHandLoosenAnim(callBack?: () => void) {
        SceneController.instance.playHandAnim(HandAnimaName.handLoosen, callBack);
    }


    //工具方法

    /**
     * 创建一个触摸事件对象
     * @param eventType 
     * @param target 
     * @returns 
     */
    getTouchEvent(eventType: string, target?: cc.Node): cc.Event.EventTouch {
        if (eventType == cc.Node.EventType.TOUCH_START) {
            this.current_touch = new cc.Touch();
        }
        let handSite: cc.Vec2 = this.handNode.convertToWorldSpaceAR(cc.Vec2.ZERO); // 获取手部节点的世界坐标
        let touchEvent = new cc.Event.EventTouch([], false); // 创建一个新的触摸事件对象
        this.current_touch.setTouchInfo(0, handSite.x, handSite.y);
        touchEvent.touch = this.current_touch;
        target && (touchEvent.target = target); // 如果提供了目标节点，则将该目标节点赋值给事件的 target 属性
        touchEvent.type = eventType; // 设置事件类型
        return touchEvent; // 返回创建好的触摸事件对象
    }

    /**
     * 用于在一个节点数组中查找符合特定条件的第一个节点
     * @param nodes 
     * @param supplementaryConditions 
     * @returns 
     */
    getBeTouchNodeByArray(nodes: cc.Node[], supplementaryConditions?: (node: cc.Node) => boolean): cc.Node {
        // let handsite = this.handNode.convertToWorldSpaceAR(cc.Vec2.ZERO); // 获取手部节点的世界坐标
        let handsiteBox = this.handNode.getBoundingBoxToWorld();
        return nodes.find(item => { // 遍历节点数组，检查每个节点是否满足条件
            if (!item.activeInHierarchy) return false; // 如果节点不是激活状态，返回false
            if (supplementaryConditions) { // 如果有补充条件，先通过补充条件检查
                if (!supplementaryConditions(item)) return false;
            }

            const _localRect = item.getBoundingBox();
            let _worldMatrix: cc.Mat4 = new cc.Mat4();
            item.parent.getWorldMatrix(_worldMatrix);
            let _worldRect: cc.Rect = new cc.Rect();
            _localRect.transformMat4(_worldRect, _worldMatrix);

            return cc.Intersection.rectRect(_worldRect, handsiteBox);
        })
    }

    /**
     * 用于在一个节点数组中查找符合特定条件的所有节点
     * @param nodes 
     * @param supplementaryConditions 
     * @returns 
     */
    getBeTouchNodesByArray(nodes: cc.Node[], supplementaryConditions?: (node: cc.Node) => boolean): cc.Node[] {
        let handsite = this.handNode.convertToWorldSpaceAR(cc.Vec2.ZERO);
        return nodes.filter(item => {
            if (!item.activeInHierarchy) return false;
            if (supplementaryConditions) {
                if (!supplementaryConditions(item)) return false;
            }

            let pos = item.parent.convertToNodeSpaceAR(handsite);
            let box = item.getBoundingBox();
            if (box.height < 50 && box.width < 50) {
                let nodesite = cc.v2(item.position);
                return pos.sub(nodesite).mag() <= 80
            } else {
                return box.contains(pos);
            }
        })
    }

    /**
     * 检查一个节点是否符合特定条件
     * @param node 
     * @param supplementaryConditions 
     * @returns 
     */
    isBeTouchNode(node: cc.Node, supplementaryConditions?: (node: cc.Node) => boolean): boolean {
        if (!node.activeInHierarchy) return false; // 检查节点是否是激活状态
        if (supplementaryConditions) { // 如果有补充条件，先通过补充条件检查
            if (!supplementaryConditions(node)) return false;
        }

        let handsite = this.handNode.convertToWorldSpaceAR(cc.Vec2.ZERO); // 获取手部节点的世界坐标

        let pos = node.parent.convertToNodeSpaceAR(handsite); // 将手部节点的世界坐标转换为节点的本地坐标
        let box = node.getBoundingBox(); // 获取节点的包围盒
        if (box.height < 50 && box.width < 50) { // 如果是小节点（高度和宽度都小于50）
            let nodesite = cc.v2(node.position); // 获取节点中心坐标
            return pos.sub(nodesite).mag() <= 80; // 检查节点中心到手部节点的距离是否在80以内
        } else { // 否则，检查手部节点的本地坐标是否在节点的包围盒内
            return box.contains(pos);
        }
    }
}


